/*
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */
package server;

import java.io.*;
import java.net.*;
import java.security.MessageDigest;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;

import com.sun.org.apache.xerces.internal.impl.dv.util.*;

// Klientske vlakno pro daemona
public class DaemonClientThread extends Thread
{
	private Socket socket = null;
	private BufferedReader in = null;
	private PrintStream out = null;

	public DaemonClientThread(Socket socket)
	{
		this.socket = socket;
		try
		{
			in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			out = new PrintStream(socket.getOutputStream());
		}
		catch (Exception e)
		{
			e.printStackTrace();
			close();
		}
	}

	// Cteni prikazu posilane daemonem
	@Override
	public void run()
	{
		sendPhrase();
		try
		{
			while (true)
			{
				String message = Sifrovani.decrypt(in.readLine());
				if (message == null)
					continue;

				if (message.contains("SystemControl"))
				{
					handle(message);
				}
				else if (message.contains("Password"))
				{
					if (!checkPassword(message))
					{
						out.println(Sifrovani.encrypt("DaemonLoginFail"));
						close();
						interrupt();
					}
				}
				else if (message.contains("DaemonControlShutdown"))
					close();
			}
		}
		catch (IOException e)
		{
		}
		finally
		{
			close();
		}
	}

	// Obsluha prikazu
	private static void handle(String msg)
	{
		String command = msg.substring(13);

		if (command.equals("Restart"))
		{
			Main.restart(true);
		}
		else if (command.equals("Shutdown"))
		{
			Main.restart(false);
		}
	}

	// Kontrola hesla
	private boolean checkPassword(String message)
	{
		String password = message.substring(8);
		Connection c = null;
		ResultSet rs = null;
		try
		{
			c = Mysql.getInstance().getConnection();
			rs = c.prepareStatement("SELECT * FROM passwords").executeQuery();

			MessageDigest md = MessageDigest.getInstance("SHA");
			byte[] newpass = password.getBytes("UTF-8");
			newpass = md.digest(newpass);
			String encpwd = Base64.encode(newpass);

			while (rs.next())
			{
				String p = rs.getString("password");
				if (p.equals(encpwd))
					return true;
			}
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
		finally
		{
			try
			{
				rs.close();
				Mysql.close(c);
			}
			catch (SQLException e)
			{
				e.printStackTrace();
			}
		}
		return false;
	}

	private void createPassword(String password)
	{
		try
		{
			MessageDigest md = MessageDigest.getInstance("SHA");
			byte[] newpass = password.getBytes("UTF-8");
			newpass = md.digest(newpass);
			Base64.encode(newpass);
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}

	}

	private void sendPhrase()
	{
		try
		{
			BufferedOutputStream bout = new BufferedOutputStream(socket.getOutputStream());
			BufferedInputStream bin = new BufferedInputStream(socket.getInputStream());
			if (readData(bin).equals("true"))
			{
				Thread.sleep(100);
				// 1st pass
				String phrase = Sifrovani.getPhrase();
				Vernam v = new Vernam();
				v.setPhrase(phrase);
				v.generateKey();
				writeData(v.XOR(), bout);
				Thread.sleep(100);
				// 2nd pass
				String b = readData(bin);
				Thread.sleep(100);
				v.setPhrase(b);
				writeData(v.XOR(), bout);
				Thread.sleep(100);
				v.forget();
			}
		}
		catch (IOException e)
		{
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}

	// Posila data pres sit ke klientovi, pouziva se pouze pro prenos sifrovaciho klice
	private void writeData(String s, BufferedOutputStream bout) throws IOException
	{
		byte[] msg = s.getBytes();
		int len = msg.length + 2;
		bout.write(len & 0xff);
		bout.write(len >> 8 & 0xff);
		bout.write(msg);
		bout.flush();
	}

	// Prijma data pres sit od klienta, pouziva se pouze pro prenos sifrovaciho klice
	private String readData(BufferedInputStream bin) throws IOException
	{
		int lengthLo = bin.read();
		int lengthHi = bin.read();
		int length = lengthHi * 256 + lengthLo;

		byte[] data = new byte[length - 2];

		int receivedBytes = 0;
		int newBytes = 0;
		int left = length - 2;
		while (newBytes != -1 && receivedBytes < length - 2)
		{
			newBytes = bin.read(data, receivedBytes, left);
			receivedBytes = receivedBytes + newBytes;
			left -= newBytes;
		}

		return new String(data);
	}

	// Uzavreni socketu, inputu a outputu
	public void close()
	{
		try
		{
			in.close();
			out.close();
			socket.close();
			interrupt();
		}
		catch (IOException e)
		{
		}
	}
}